package wiki.xsx.core.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.DefaultLettucePool;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePool;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import redis.clients.jedis.JedisPoolConfig;
import wiki.xsx.core.util.ApplicationContextUtil;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * redis自动配置
 * @author xsx
 * @date 2019/4/29
 * @since 1.8
 */
@Configuration
public class RedisAutoConfiguration {

    private static final Logger log = LoggerFactory.getLogger(RedisAutoConfiguration.class.getName());

    /**
     * 上下文工具
     * @return
     */
    @Bean
    public ApplicationContextUtil applicationContextUtil() {
        return new ApplicationContextUtil();
    }

    /**
     * redis对象模板
     * @param redisConnectionFactory 连接工厂
     * @return 返回redis对象模板
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setEnableDefaultSerializer(true);
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * redis字符串模板
     * @param redisConnectionFactory 连接工厂
     * @return 返回redis字符串模板
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * 连接工厂
     * @param redisProperties redis默认配置
     * @return 返回连接工厂
     */
    @Bean
    @Scope("prototype")
    public RedisConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
        if (redisProperties.isJedisConfig()) {
            return this.getJedisConnectionFactory(redisProperties);
        }else if (redisProperties.isLettuceConfig()) {
            return this.getLettuceConnectionFactory(redisProperties);
        }else {
            log.warn("未找到相关连接工厂,将使用默认工厂配置");
            return this.getLettuceConnectionFactory(redisProperties);
        }
    }

    /**
     * redis默认配置
     * @return 返回redis默认配置
     * @throws IOException
     */
    @Bean
    public RedisProperties redisProperties() throws IOException {
        Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource("redis.properties"));
        RedisProperties redisProperties = new RedisProperties();
        // 基本信息设置
        String database = properties.getProperty("redis.database");
        if (StringUtils.hasText(database)) {
            redisProperties.setDatabase(Integer.valueOf(database));
        }
        String url = properties.getProperty("redis.url");
        if (StringUtils.hasText(url)) {
            redisProperties.setUrl(url);
        }
        String host = properties.getProperty("redis.host");
        if (StringUtils.hasText(host)) {
            redisProperties.setHost(host);
        }
        String port = properties.getProperty("redis.port");
        if (StringUtils.hasText(port)) {
            redisProperties.setPort(Integer.valueOf(port));
        }
        String password = properties.getProperty("redis.password");
        if (StringUtils.hasText(password)) {
            redisProperties.setPassword(password);
        }
        String timeout = properties.getProperty("redis.timeout");
        if (StringUtils.hasText(timeout)) {
            redisProperties.setTimeout(Long.valueOf(timeout));
        }
        String ssl = properties.getProperty("redis.ssl");
        if (StringUtils.hasText(ssl)) {
            redisProperties.setSsl(Boolean.valueOf(ssl));
        }
        boolean usePool = true;
        String isUsePool = properties.getProperty("redis.pool.usePool");
        if (StringUtils.hasText(isUsePool)) {
            usePool = Boolean.valueOf(isUsePool);
            redisProperties.setUsePool(usePool);
        }
        if (usePool) {
            // 连接池设置
            RedisProperties.Pool pool = new RedisProperties.Pool();
            String maxIdle = properties.getProperty("redis.pool.maxIdle");
            if (StringUtils.hasText(maxIdle)) {
                pool.setMaxIdle(Integer.valueOf(maxIdle));
            }
            String minIdle = properties.getProperty("redis.pool.minIdle");
            if (StringUtils.hasText(minIdle)) {
                pool.setMinIdle(Integer.valueOf(minIdle));
            }
            String maxActive = properties.getProperty("redis.pool.maxActive");
            if (StringUtils.hasText(maxActive)) {
                pool.setMaxActive(Integer.valueOf(maxActive));
            }
            String maxWait = properties.getProperty("redis.pool.maxWait");
            if (StringUtils.hasText(maxWait)) {
                pool.setMaxWait(Long.valueOf(maxWait));
            }
            String jedisConfig = properties.getProperty("redis.pool.jedisConfig");
            if (StringUtils.hasText(jedisConfig)) {
                redisProperties.setJedisConfig(Boolean.valueOf(jedisConfig));
            }
            redisProperties.getJedis().setPool(pool);
            String lettuceConfig = properties.getProperty("redis.pool.lettuceConfig");
            if (StringUtils.hasText(lettuceConfig)) {
                redisProperties.setLettuceConfig(Boolean.valueOf(lettuceConfig));
                if (!redisProperties.isLettuceConfig()) {
                    redisProperties.setJedisConfig(true);
                }
            }
            redisProperties.getLettuce().setPool(pool);
            String shutdownTimeout = properties.getProperty("redis.pool.shutdownTimeout");
            if (StringUtils.hasText(shutdownTimeout)) {
                redisProperties.getLettuce().setShutdownTimeout(Long.valueOf(shutdownTimeout));
            }
        }

        // 哨兵设置
        RedisProperties.Sentinel sentinel = new RedisProperties.Sentinel();
        String master = properties.getProperty("redis.sentinel.master");
        if (StringUtils.hasText(master)) {
            sentinel.setMaster(master);
            String sentinelNodes = properties.getProperty("redis.sentinel.nodes");
            if (StringUtils.hasText(sentinelNodes)) {
                sentinel.setNodes(Arrays.asList(sentinelNodes.split(",")));
            }
        }
        if (sentinel.getMaster()!=null&&sentinel.getNodes()!=null) {
            redisProperties.setSentinel(sentinel);
        }

        // 集群设置
        RedisProperties.Cluster cluster = new RedisProperties.Cluster();
        String clusterNodes = properties.getProperty("redis.cluster.nodes");
        if (StringUtils.hasText(clusterNodes)) {
            cluster.setNodes(Arrays.asList(clusterNodes.split(",")));
            String maxRedirects = properties.getProperty("redis.cluster.maxRedirects");
            if (StringUtils.hasText(maxRedirects)) {
                cluster.setMaxRedirects(Integer.valueOf(maxRedirects));
            }
        }
        if (cluster.getMaxRedirects()!=null&&cluster.getNodes()!=null) {
            redisProperties.setCluster(cluster);
        }
        return redisProperties;
    }

    /**
     * 获取jedis连接工厂
     * @param redisProperties redis默认配置
     * @return 返回jedis连接工厂
     */
    private JedisConnectionFactory getJedisConnectionFactory (RedisProperties redisProperties) {
        JedisConnectionFactory factory;
        if (redisProperties.getSentinel()!=null) {
            if (redisProperties.isUsePool()) {
                factory = new JedisConnectionFactory(
                        this.getSentinelConfig(redisProperties),
                        this.getJedisPoolConfig(redisProperties)
                );
            }else {
                factory = new JedisConnectionFactory(this.getSentinelConfig(redisProperties));
            }
        }else {
            if (redisProperties.isUsePool()) {
                factory = new JedisConnectionFactory(this.getJedisPoolConfig(redisProperties));
            }else {
                factory = new JedisConnectionFactory();
            }
        }
        factory.setDatabase(redisProperties.getDatabase());
        factory.setHostName(redisProperties.getHost());
        factory.setPort(redisProperties.getPort());
        factory.setPassword(redisProperties.getPassword());
        factory.setTimeout((int) redisProperties.getTimeout());
        factory.afterPropertiesSet();
        return factory;
    }

    /**
     * 获取lettuce连接工厂
     * @param redisProperties redis默认配置
     * @return 返回lettuce连接工厂
     */
    public LettuceConnectionFactory getLettuceConnectionFactory(RedisProperties redisProperties) {
        LettuceConnectionFactory factory;
        if (redisProperties.isUsePool()) {
            factory = new LettuceConnectionFactory(this.getLettucePoolConfig(redisProperties));
        }else{
            factory = new LettuceConnectionFactory();
        }
        factory.setDatabase(redisProperties.getDatabase());
        factory.setHostName(redisProperties.getHost());
        factory.setPort(redisProperties.getPort());
        factory.setPassword(redisProperties.getPassword());
        factory.setTimeout((int) redisProperties.getTimeout());
        factory.afterPropertiesSet();
        return factory;
    }

    /**
     * 获取jedis连接池配置
     * @param redisProperties redis默认配置
     * @return 返回jedis连接池配置
     */
    private JedisPoolConfig getJedisPoolConfig(RedisProperties redisProperties) {
        RedisProperties.Pool pool = redisProperties.getJedis().getPool();
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(pool.getMaxIdle());
        poolConfig.setMinIdle(pool.getMinIdle());
        poolConfig.setMaxTotal(pool.getMaxActive());
        poolConfig.setMaxWaitMillis(pool.getMaxWait());
        return poolConfig;
    }

    /**
     * 获取lettuce连接池配置
     * @param redisProperties redis默认配置
     * @return 返回lettuce连接池配置
     */
    private LettucePool getLettucePoolConfig(RedisProperties redisProperties) {
        DefaultLettucePool poolConfig = new DefaultLettucePool(
                redisProperties.getHost(),
                redisProperties.getPort(),
                this.getJedisPoolConfig(redisProperties)
        );
        poolConfig.afterPropertiesSet();
        return poolConfig;
    }

    /**
     * 获取哨兵配置
     * @param redisProperties redis默认配置
     * @return 返回哨兵配置
     */
    private RedisSentinelConfiguration getSentinelConfig(RedisProperties redisProperties) {
        RedisProperties.Sentinel sentinel = redisProperties.getSentinel();
        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
        sentinelConfig.setMaster(sentinel.getMaster());
        sentinelConfig.setSentinels(this.getNodeList(sentinel.getNodes()));
        return sentinelConfig;
    }

    /**
     * 获取节点列表
     * @param nodes 节点字符串
     * @return 返回节点列表
     */
    private List<RedisNode> getNodeList(List<String> nodes) {
        List<RedisNode> nodeList = new ArrayList<>(nodes.size());
        for (String node : nodes) {
            String[] n = node.split(":");
            nodeList.add(new RedisNode(n[0], Integer.valueOf(n[1])));
        }
        return nodeList;
    }
}
